<script setup>
import _ from 'lodash'
// import draggable from 'vuedraggable';
import {ref, reactive, computed, watch, onMounted, nextTick, getCurrentInstance, toRefs, onUnmounted} from 'vue'
import {
  isMicroComp,
  isDataNode,
  getNodeCompTpl,
  resetCode,
  _getAlias,
  getNodeType,
  getNodeTypeIcon,
  getNodeTypeLabel,
  isMultipleNode,
  isModelNode,
  isLeafNode,
  initNodeDefSort,
  addChildNodeByType
} from '../utils/mutil'
import tplCate from '../utils/tpl-cate'
import {useCommonStore} from "@jzl/stores";
import {DFT_ICONS} from "@jzl/icons";
import { $utils, $bus } from '@jzl/utils'
import {$WinFileSpace} from "@jzl/comps";
import BuilderNodesProp from './builder-nodes-prop.vue'
import BuilderNodesAdd from './builder-nodes-add.vue'
import NodeDataValue from './ele/node-data-value.vue'
import BuilderNodePropext from './builder-node-propext.vue'

import BuilderNodesLowcode from './builder-nodes-lowcode.vue'
import NodeFormulaRender from './ele/node-formula-render.vue'
import BuilderCompsCloud from './builder-comps-cloud.vue'

// import { IconPanel } from "@jzl/comps";

const instance = getCurrentInstance()
// Props
const props = defineProps({
  pageType: [String, Number],
  currentItem: Object,
  currentPage: Object,
  currentItemElCode: String,
  currentItems: Array,
  currentNode: {
    type: Object,
    default: {}
  },
  elemNodes: {
    type: [Array, Object],
    default: () => []
  },
  pageWidth: {
    type: Number,
    default: 1920
  },
  pageHeight: {
    type: Number,
    default: 1080
  },
  container: {
    type: Object,
    default: null
  }
})

// Emits
const emit = defineEmits([
  'pinMenuChange',
  'hoverElenode',
  'selectElenode',
  'selectItem',
  // 'eleNodeTreeChange',
  // 'changeElemNodes',
  'create-page-item',
  'pinPropChange'
])

const nodeTreeSetting = {
  label: 'name',
  // children: 'childs'
}

// Refs
const nodesTree = ref(null)
const nodeTreeDragHandler = ref(null)
const nodeMorePopoverRef = ref(null)
const nodeMoreTriggerRef = ref(null)

const nodesTreeContainer = ref(null)

const nodeActionPopoverRef = ref(null)
const nodeActionTriggerRef = ref(null)

// State
const state = reactive({
  collapse: false,
  currentEleData: {},
  // currentNode: {},
  expandKeys: [],
  popData: null,
  nodesPropShow: true,
  propTab: 'style',
  moving: false,
  tempExpand: false,
  hoverExpand: false,
  panelFixed: true,
  tempExpandTimer: null,
  hoverExpandTimer: null,
  isNodeHover: false,
  hoverTimeout: null,

  // Popover visibility
  nodeActionPopVisible: false,
  nodeMorePopVisible: false,
  nodeValuePopVisible: false,
  nodeEventPopVisible: false,
  nodeLowcodePopVisible: false,
  nodeFormulaPopVisible: false,

  compPanelName: "选择云组件",
  compAddPanelShow: false,
  compPanelShow: false,
  isCreateMicroComp: false,
  pageItemSelectWinShow: false,
})

const {
  collapse,
  currentEleData,
  // currentNode,
  expandKeys,
  popData,
  nodesPropShow,
  propTab,
  moving,
  tempExpand,
  hoverExpand,
  panelFixed,
  tempExpandTimer,
  hoverExpandTimer,
  isNodeHover,
  hoverTimeout,

  // Popover visibility
  nodeActionPopVisible,
  nodeMorePopVisible,
  nodeValuePopVisible,
  nodeEventPopVisible,
  nodeLowcodePopVisible,
  nodeFormulaPopVisible,

  compPanelName,
  compAddPanelShow,
  compPanelShow,
  // isCreateMicroComp,
  pageItemSelectWinShow,
} = toRefs(state)

// Computed
const isDataEnable = computed(() => {
  return isDataNode(props.currentNode?.def)
})

// Drag & Drop Config
const nodeTreeDragProp = reactive({
  boundingElement: document.body,
  handle: null,
  onPositionChange,
  onDragStart: () => {
    state.moving = true
  },
  onDragEnd: (positionDiff, absolutePosition, event) => {
    const $el = instance?.proxy?.$el
    state.moving = false
    if (absolutePosition && absolutePosition.top < 0) {
      $el.style.top = "0px"
    }
    if (absolutePosition && absolutePosition.left < 100) {
      $el.style.left = "0px"
      $el.style.top = "41px"
      state.panelFixed = true
      emit('pinMenuChange', true)
    } else {
      state.panelFixed = false
      emit('pinMenuChange', false)
    }
  }
})

// Mounted
onMounted(() => {
  nextTick(() => {
    nodeTreeDragProp.handle = nodeTreeDragHandler.value
  })

  $bus.on('design:clickBody', () => {
    hideNodeActionPopover()
  })
  $bus.on('design:clickNode', (param) => {
    if(!nodeActionPopVisible.value) showNodeActionPopover()
  })
})

// Watchers
watch(
  () => props.currentItemElCode,
  (code) => {
    nodesTree.value?.setCurrentKey(code)
    if (!state.isNodeHover) state.expandKeys = [code]

    // const nodeData = $utils.getTreeData(props.elemNodes, 'code', code)
    // const currentEleData = _.get(nodeData, 'def')
    // state.currentEleData = currentEleData || {}
    // state.currentNode = nodeData
    // state.popData = nodeData
    handleTempExpand()
    showNodeActionPopover()
  }
)

watch(() => props.currentNode, (cn) => {
  state.popData = cn
  state.currentEleData = cn?.def || {}
}, { deep: true })

watch(
  () => state.nodesPropShow,
  (val) => {
    if (!val) return
    const $el = instance?.proxy?.$el
    const screenWidth = window.screen.width
    const rect = $el.getBoundingClientRect()
    const left = rect.left
    const right = rect.right
    const distance = 350 - (screenWidth - right)
    if (distance > 0 && $el && $el.style) {
      $el.style.left = `${left - distance}px`
    }
  }
)

// Methods
function onPositionChange() {
  hideNodeMorePopover()
}

function hasActionLimitData(data, actionType = 'div') {
  const nodeType = _.get(data, 'def._type') || _.get(data, 'def._node') || _.get(data, 'def._component')
  return !['text', 'icon', 'img', 'video'].includes(nodeType)
}

function hasActionLimit(actionType) {
  return hasActionLimitData(state.popData, actionType)
}

function getCurrentItem(node) {
  if (!node) return
  const isRoot = _.get(node, 'data.isRoot')
  const item = _.get(node, 'data.item')
  const items = _.get(node, 'data.items')
  const parent = _.get(node, 'parent')
  return isRoot ? {item, items} : getCurrentItem(parent)
}

function clearSelectEleNodeHoverTimeout() {
  if (state.hoverTimeout) {
    clearTimeout(state.hoverTimeout)
    state.hoverTimeout = null
  }
}

function handleSelectEleNodeHover(data, node) {
  emit('hoverElenode', data)
}

function handleSelectEleNodeClick(data, node, vueObj, isHover = false) {
  const nodeCode = _.get(data, 'code')
  state.isNodeHover = isHover || false
  if (!state.isNodeHover) state.expandKeys = [nodeCode]
  emit('selectElenode', data)

  const info = getCurrentItem(node)
  if (info) emit('selectItem', info)

  state.popData = data
  const currentCode = _.get(data, 'code')
  const popCode = _.get(state.popData, 'code')
  if (currentCode !== popCode) {
    hideNodeMorePopover()
  }
}

function showNodeMorePopover(e, data, isActionBtn = true) {
  if (!isActionBtn) state.popData = data

  const closestEle = e.target.closest('.menu-custom-tree-node') || e.target.closest('span');
  nodeMoreTriggerRef.value = isActionBtn ? closestEle : nodesTreeContainer.value;
  nodeMorePopVisible.value = true;
}

function hideNodeMorePopover() {
  nodeMorePopVisible.value = false;
  nodeMoreTriggerRef.value = null;
}

function getClosestVisibleEle(ele, idx = 0) {
  if (!ele) return null
  if (idx > 5) return null
  if (!idx) idx = 0
  const style = window.getComputedStyle(ele)
  return style.display === 'none' ? getClosestVisibleEle(ele.parentNode, ++idx) : ele
}

function showNodeActionPopover() {
  const focusCode = props.currentItemElCode
  if (!focusCode) return

  const $eles = document.querySelectorAll(`[j-name=${focusCode}]`)
  const $refer = getClosestVisibleEle(_.get($eles, '[0]'))
  if (!$refer) return

  nodeActionPopVisible.value = true;
  nodeActionTriggerRef.value = $refer;
}

function hideNodeActionPopover() {
  nodeActionPopVisible.value = false;
  nodeActionTriggerRef.value = null;
}

const handleSelectIcon = (icon) => {
  if (state.currentEleData && state.currentEleData._ss) {
    state.currentEleData._ss.icon = icon
  }
}

function handleShowNodeIconWin() {
  $IconSpace({
    onSelect: (icon) => {
      if (state.currentEleData && state.currentEleData._ss) {
        state.currentEleData._ss.icon = icon
      }
    }
  })
}

function handleShowNodeImgWin() {
  $WinFileSpace({
    onConfirm: (selected) => {
      const img = _.isArray(selected) ? selected[0] : selected
      const filePath = img?.file_path
      if (state.currentEleData && state.currentEleData._ss && filePath) {
        state.currentEleData._ss.image = filePath
      }
    }
  })
}

function handleClearNodeProp(type) {
  if (!state.currentEleData || !state.currentEleData._ss) return
  if (type === 'icon') state.currentEleData._ss.icon = ''
  else if (type === 'img') state.currentEleData._ss.image = ''
}

function isFixNode(data) {
  const def = _.get(data, 'def')
  const code = _.get(data, 'code') || _.get(def, '_code')
  const isFix = _.get(def, '_fix')
  if (!code || isFix) return true

  const nodes = _.get(window, '$utils.getTreePath')(code, props.elemNodes, 'code')
  const idx = _.findIndex(nodes, {_fix: 'all'})
  return idx >= 0
}

function getCurrentItemIndex() {
  const itemCode = _.get(props.currentItem, 'page_item_code')
  return itemCode ? _.findIndex(props.currentItems, {page_item_code: itemCode}) : -1
}

function handleRemoveSelected() {
  handleRemove(state.popData)
}

function handleRemoveItem(item) {
  const itemCode = _.get(item, 'page_item_code')
  if (!itemCode) return
  const idx = _.findIndex(props.currentItems, {page_item_code: itemCode})
  if (idx >= 0) {
    props.currentItems.splice(idx, 1)
    emit('selectItem', {})
    // emit('eleNodeTreeChange')
    $bus.emit('design:changeElemNodes')
    $bus.emit('design:clearMask')

    hideNodeActionPopover()
  }
}

function handleRemove(data) {
  if (!data) return
  if (data.isRoot) return handleRemoveItem(data.item)

  const def = _.get(data, 'def')
  const parentDef = _.get(data, 'parentDef')
  const code = _.get(data, 'code')

  if (!def || !parentDef || !code) return

  for (const p in parentDef) {
    const child = _.get(parentDef, p)
    if (child && child._code === code) {
      let delInfo = parentDef[p];
      if(delInfo && delInfo._lib) {
        if(parentDef?._ss?.theme) {
          parentDef._ss.theme = (parentDef._ss?.theme || '').split(' ')
            .filter((v) => v !== `${ delInfo._lib }-theme`).join(' ')
        }
      }
      delete parentDef[p]
      break
    }
  }

  const expandKeys = [_.get(parentDef, '_code')]
  for (const p in parentDef) {
    const child = _.get(parentDef, p)
    if (child && child._code) expandKeys.push(child._code)
  }

  hideNodeMorePopover()
  hideNodeActionPopover()

  $bus.emit('design:changeElemNodes')
  $bus.emit('design:clearMask')

  nextTick(() => {
    state.expandKeys = expandKeys
  })
}

function handleNameChg(data) {
  console.log('handleNameChg', data)
  if (!data) return
  const name = _.get(data, 'name')
  const def = _.get(data, 'def')
  if (!def) return
  data.nameEditing = false
  def._name = name
}

function getNodeRootCode(node) {
  const root = _.get(window, 'getItemRootByNode')(node)
  return _.get(root, 'data.code')
}

function allowDrop(draggingNode, dropNode, type) {
  const dropData = _.get(dropNode, 'data')
  const dropDataDef = _.get(dropData, 'def')
  const dropNodeIsRoot = _.get(dropData, 'isRoot')
  const dropNodeName = _.get(dropData, 'name')
  const dropNodeType = _.get(dropDataDef, '_type') || _.get(dropDataDef, '_node')
  const dragIsRoot = _.get(draggingNode, 'data.isRoot')

  if (type === 'inner') {
    if (['text', 'icon', 'img'].includes(dropNodeType)) return false
    if (dragIsRoot) {
      if (dropNodeName && dropNodeName !== 'jzlLayout') return false
    } else {
      if (!dropNodeName || dropNodeName === 'jzlLayout') return false
    }

    const subs = _.get(dropData, 'children')
    if (subs && subs.length) {
      const hasMulti = _.find(subs, s => ['grid', 'slider'].includes(_.get(s, 'def._type')))
      if (hasMulti) return false
    }
  } else if (type === 'prev' || type === 'next') {
    if (dragIsRoot && !_.get(dropNode, 'data.isRoot')) return false
    if (!dragIsRoot && _.get(dropNode, 'data.isRoot')) return false

    const parentName = _.get(dropNode, 'parent.data.name')
    const nodeType = _.get(dropDataDef, '_type')
    if ((parentName === 'jzlLayout' || !parentName) && !_.get(dropData, 'isRoot')) return false
    if (['grid', 'slider'].includes(nodeType)) return false
  }

  if (!dragIsRoot) {
    const dragRootCode = getNodeRootCode(draggingNode)
    const dropRootCode = getNodeRootCode(dropNode)
    if (!dragRootCode || dragRootCode !== dropRootCode) return false
  }

  return true
}

function allowDrag(draggingNode) {
  const nodeData = _.get(draggingNode, 'data')
  const nodeDef = _.get(nodeData, 'def')
  const isRoot = _.get(nodeData, 'isRoot')
  const nodePropName = _.get(nodeData, 'propName')
  const nodeType = _.get(nodeDef, '_node')
  return isRoot || !['grid', 'slider', 'box_style'].includes(nodeType) && nodePropName !== 'box_style'
}

function handleDropDone(draggingNode, dropNode, type, event) {
  emit('node-drop-done', {draggingNode, dropNode, type, event})
}

function getPageTitle(page, dftTitle) {
  return _.get(page, 'page_name') || dftTitle
}

function isEmptyPage(info) {
  return !info || !info.page_id
}

function closeOver() {
  state.compAddPanelShow = false
}

function hideNodeProp() {
  state.nodesPropShow = false
}

function showNodeProp(propTab = 'style', isHover = false) {
  state.nodesPropShow = true
  state.collapse = false
  if (!isHover || propTab !== 'style' || state.propTab !== 'hover') {
    state.propTab = propTab
  }
}

function toggleNodeProp() {
  state.nodesPropShow = !state.nodesPropShow
  emit('pinPropChange', state.nodesPropShow)
}

function handleCollapseSwitch() {
  state.collapse = !state.collapse
}

function handleTempExpand() {
  state.tempExpand = true
  if (state.tempExpandTimer) clearTimeout(state.tempExpandTimer)
  state.tempExpandTimer = setTimeout(() => {
    state.tempExpand = false
  }, 1200)
}

function handleMouseEnter() {
  state.hoverExpand = true
}

function handleMouseLeave() {
  if (state.hoverExpandTimer) clearTimeout(state.hoverExpandTimer)
  state.hoverExpandTimer = setTimeout(() => {
    state.hoverExpand = false
  }, 1000)
  emit('hoverElenode', null)
}

function handlePropTabChange(tab) {
  state.propTab = tab
}

function copyNode(data) {
  const prop = _.get(data, 'propName')
  const def = _.cloneDeep(_.get(data, 'def'))
  if (!prop || !def) return
  _.get(window, 'resetCode')(def)
  const str = JSON.stringify({prop, def})
  _.get(window, '$utils.copyText')(str)
}

async function pasteNode(data) {
  try {
    const copiedText = await navigator.clipboard.readText()
    const json = JSON.parse(copiedText)
    const prop = _.get(json, 'prop')
    const def = _.get(json, 'def')
    if (!prop || !def) return
    const xDef = _.get(data, 'def')
    if (xDef[prop]) {
      prop = _.get(window, '$utils.getNanoid')(8)
    }
    xDef[prop] = def
    // emit('eleNodeTreeChange')
    $bus.emit('design:changeElemNodes')
  } catch (e) {
    console.error(e)
  }
}

function copyItem() {
  const idx = getCurrentItemIndex()
  if (idx < 0) return
  const orig = _.cloneDeep(props.currentItems[idx])
  const code = _.get(window, '$utils.getNanoid')(8)
  const citem = _.defaultsDeep({
    page_item_code: code,
    alias: _.get(window, '_getAlias')(code, props.items),
    project_id: props.projectId,
    module_name: code,
    i: code
  }, orig)
  delete citem.page_item_id
  const ele_style = _.get(citem, 'ele_style')
  _.get(window, 'resetCode')(ele_style)
  props.currentItems.splice(idx + 1, 0, citem)
  // emit('eleNodeTreeChange')
  $bus.emit('design:changeElemNodes')
}

function handleCtrlC(data) {
  if (!data) return
  if (data.isRoot) copyItem(data)
  else copyNode(data)
}

function handleAddItem(menu, type) {
  let def = _.get(menu, 'def')
  const vars = _.get(menu, 'vars')
  const component = _.get(menu, 'component')
  if (!def) return

  let wrapDef = _.cloneDeep(def)
  const varsDef = _.cloneDeep(vars)

  if (component !== 'div' && component !== 'box') {
    if (type === 'box') {
      wrapDef = getNodeCompTpl('box')
      wrapDef.body[component] = def
    } else if (type === 'div') {
      wrapDef = getNodeCompTpl('div')
      wrapDef[component] = def
    }
  }

  if (props.pageType == 1) {
    let width = _.get(wrapDef._ss, 'width') || 300
    let height = _.get(wrapDef._ss, 'height') || 300

    if (component === 'button') {
      width = 200
      height = 50
    } else if (component === 'icon') {
      width = 50
      height = 50
    } else if (component === 'text') {
      width = 300
      height = 50
    } else if (component === 'img') {
      width = 300
      height = 300
    } else if (component === 'c$navComp') {
      width = 500
      height = 100
    }

    if(type === 'box') {
      width = 300;
      height = 200;
    }

    if (!wrapDef._ss) wrapDef._ss = {}
    wrapDef._ss.position = 'absolute'
    wrapDef._ss.width = width
    wrapDef._ss.height = height

    const pageWidth = props.pageWidth || 1920
    const pageHeight = props.pageHeight || 1080

    // 默认中间
    let left = (pageWidth - width) / 2
    let top = (pageHeight - height) / 2

    // 有 container ， 在container 的可视化中间
    if(props.container) {
      let containerRect = props.container.getBoundingClientRect();
      let containerWidth = containerRect.width;
      let containerHeight = containerRect.height;
      // console.log(props.container.getBoundingClientRect(), props.container.scrollLeft, props.container.scrollTop)
      left = (containerWidth - width) / 2 + props.container.scrollLeft;
      top = (containerHeight - height) / 2 + props.container.scrollTop;
    }

    wrapDef._ss.left = left
    wrapDef._ss.top = top
  }

  emit('create-page-item', {
    component: 'JzlItemComp',
    micro: true,
    name: '模块',
    ele_style: wrapDef,
    vars: varsDef
  })

  state.compAddPanelShow = false
}

function handleAddChildNodeCloud(compInfo) {
  const comp_lib = _.get(compInfo, 'comp_lib')
  const comp_name = _.get(compInfo, 'comp_name')
  const comp_def = _.get(compInfo, 'comp_def')
  if (!state.popData) return

  const def = _.get(state.popData, 'def')
  const childDef = addChildNodeByType(comp_def, def, props.currentItem, { _lib: comp_lib, _node: comp_name })
  const _code = childDef?._code

  def._ss.theme = [ ...(def._ss?.theme || '').split(' '), `${ comp_lib }-theme`]
    .filter((v, i, arr) => arr.indexOf(v) === i && v).join(' ')

  nextTick(() => {
    setTimeout(() => {
      emit('selectItem', {
        targetEleName: _code,
        item: props.currentItem,
        items: props.currentItems
      })
    }, 100)
  })
}

function handleShowMicroComps() {
  state.collapse = false
  state.compAddPanelShow = true
}

function handleShowCloudComps(isCreateMicroComp = false) {
  state.collapse = false
  state.compPanelShow = true
  state.isCreateMicroComp = isCreateMicroComp
}

function handleCreateCloudComp(compInfo) {
  if (state.isCreateMicroComp) {
    handleAddChildNodeCloud(compInfo)
  } else {
    const comp_lib = _.get(compInfo, 'comp_lib')
    const comp_name = _.get(compInfo, 'comp_name')
    const comp_def = _.get(compInfo, 'comp_def')

    const info = _.get(comp_def, 'ele_style')
    const vars = _.get(comp_def, 'vars')
    const _code = $utils.getNanoid(10)
    const component = _.get(comp_def, 'component') || comp_name

    info._code = _code
    info._lib = comp_lib
    info._node = comp_name
    // info._type = comp_name
    info._name = `组件-${comp_name}`

    const eleDef = _.cloneDeep(info)
    const varsDef = _.cloneDeep(vars)
    const wrapDef = getNodeCompTpl('div')
    wrapDef[component] = eleDef
    wrapDef._ss.theme = [ ...(wrapDef._ss?.theme || '').split(' '), `${ comp_lib }-theme`]
      .filter((v, i, arr) => arr.indexOf(v) === i && v).join(' ')

    emit('create-page-item', {
      component: 'JzlItemComp',
      micro: true,
      name: '模块',
      ele_style: wrapDef,
      vars: varsDef
    })
  }
  $jui.toast('添加成功！')
}

function handleCloseCompPanel() {
  state.compAddPanelShow = false
}

function handleClearTagValue(nodeData) {
  if (nodeData && nodeData._value) nodeData._value = ''
}

function handlePageItemSelectShow() {
  state.pageItemSelectWinShow = true
}

function handleCopyItem(item) {
  const code = $utils.getNanoid(10)
  const copyItem = _.defaultsDeep({
    page_item_code: code,
    alias: _getAlias(code),
    project_id: props.projectId,
    module_name: code,
    i: code
  }, item)
  delete copyItem.page_item_id
  const boxStyle = _.get(copyItem, 'box_style')
  const eleStyle = _.get(copyItem, 'ele_style')
  resetCode(boxStyle)
  resetCode(eleStyle)
  handleMenuClick(copyItem)
  state.pageItemSelectWinShow = false
}

function handleReferItem(item) {
  const code = $utils.getNanoid(10)
  const referItem = _.defaultsDeep({
    page_item_code: code,
    alias: _getAlias(code),
    module_name: code,
    i: code,
    project_id: props.projectId,
    refer: item,
    refer_item_id: _.get(item, 'page_item_id')
  }, _.cloneDeep(referDef))
  handleMenuClick(referItem)
  state.pageItemSelectWinShow = false
}

function isCurrentSelected(data) {
  const isRoot = _.get(data, 'isRoot')
  const pic = _.get(data, 'item.page_item_code')
  const pic2 = _.get(props.currentItem, 'page_item_code')
  return isRoot && pic && pic === pic2
}

/*const handleChangeElemNodesTest = () => {
  emit('changeElemNodes')
}*/

// Expose methods
defineExpose({
  onPositionChange,
  handleTempExpand,
  showNodeActionPopover,
  hideNodeMorePopover,
  hideNodeActionPopover,
  handleSelectEleNodeHover,
  handleSelectEleNodeClick,
  handleShowNodeIconWin,
  handleShowNodeImgWin,
  handleClearNodeProp,
  isFixNode,
  handleRemove,
  handleRemoveItem,
  handleCtrlC,
  handleAddItem,
  handleAddChildNodeCloud,
  handleShowMicroComps,
  handleShowCloudComps,
  handleCreateCloudComp,
  handleCloseCompPanel,
  handleClearTagValue,
  handlePageItemSelectShow,
  handleCopyItem,
  handleReferItem,
  isCurrentSelected
})
</script>

<template>
  <div
    class="ele-design-nodes"
    :class="{
      'fixed': panelFixed,
      'collapse': collapse,
      'expand': !collapse,
      'moving': moving,
      'temp-expand': tempExpand,
      'hover-expand': hoverExpand
    }"
    v-draggable="nodeTreeDragProp"
    v-click-outside="closeOver"
    @mouseenter.stop="handleMouseEnter"
    @mouseleave.stop="handleMouseLeave"
  >
    <div
      class="elem-node-tree"
      ref="nodesTreeContainer"
    >
      <div
        class="elem-node-header elem-node-header-move"
        ref="nodeTreeDragHandler"
        @dblclick="handleCollapseSwitch"
      >
        <div class="elem-node-tab">
          <!--<i class="fa fa-grip-vertical elem-node-move"></i>-->
          <el-popover
            v-model:visible="compAddPanelShow"
            placement="bottom-start"
            width="300"
            trigger="manual"
          >
            <template #reference>
              <div
                class="elem-node-mulicon elem-node-move"
                @mousedown.stop
                @click.stop="handleShowMicroComps"
                v-click-outside="handleCloseCompPanel"
                v-tippy="{ arrow: true, placement: 'top' }"
                content="添加新模块"
              >
                <JIcon
                  icon="ri:apps-2-add-line"
                />
                模块
              </div>
            </template>

            <el-scrollbar
              class="ele-design-prop-eles ele-design-eles-pop">
              <h5>添加新模块</h5>
              <template v-for="(eles, prop) in tplCate">
                <template v-if="prop !== 'formElems'">
                  <ul>
                    <el-row :gutter="5" class="w-full">
                      <el-col
                        v-for="ele in eles"
                        :gutter="10"
                        :span="12"
                      >
                        <li
                          :key="ele.component"
                          @click="handleAddItem(ele)"
                        >
                          <JIcon :icon="ele.icon"></JIcon>
                          <p v-text="ele.name"></p>
<!--                          <template v-if="pageType !== 1">-->
                            <JIcon
                              :icon="DFT_ICONS.div"
                              class="action-box"
                              v-if="ele.component !== 'div' && ele.component !== 'c$boxComp'"
                              @click.stop="handleAddItem(ele, 'div')"
                              v-tippy="{ arrow: true, placement: 'top' }"
                              content="DIV容器中"
                            />
                            <JIcon
                              :icon="DFT_ICONS.box"
                              class="action-box"
                              v-if="ele.component !== 'div' && ele.component !== 'c$boxComp'"
                              @click.stop="handleAddItem(ele, 'box')"
                              v-tippy="{ arrow: true, placement: 'top' }"
                              content="Box容器中"
                            />
<!--                          </template>-->
                        </li>
                      </el-col>
                    </el-row>
                  </ul>
                  <hr class="mt-2 mb-1" />
                </template>
              </template>
            </el-scrollbar>
          </el-popover>
          <div
            class="elem-node-mulicon elem-node-pluscloud"
            @mousedown.stop
            @click.stop="handleShowCloudComps(false)"
            v-tippy="{ placement: 'top' }"
            content="添加云模块"
          >
            <JIcon :icon="DFT_ICONS.cloud" class="jwdicon"/>
            云模块
          </div>
          <div
            class="elem-node-mulicon elem-node-pluscloud"
            @mousedown.stop
            @click.stop="handlePageItemSelectShow(false)"
            v-tippy="{ placement: 'top' }"
            content="添加已有页面中模块"
          >
            <JIcon icon="ri:sticky-note-add-line"/>
            页模块
          </div>
        </div>
      </div>
      <el-scrollbar
        class="ele-design-body-items xthin-scrollbar h-full"
        @mouseleave="clearSelectEleNodeHoverTimeout"
      >
        <el-tree
          ref="nodesTree"
          class="menu-custom-tree builder-nodes-tree"
          :data="elemNodes"
          node-key="code"
          indent="10"
          :highlight-current="true"
          :props="nodeTreeSetting"
          :expand-on-click-node="false"
          :auto-expand-parent="true"
          :default-expanded-keys="expandKeys"
          @node-click="handleSelectEleNodeClick"
          @node-drop="handleDropDone"
          draggable
          empty-text="点击左上方按钮，添加新的模块"
          :allow-drop="allowDrop"
          :allow-drag="allowDrag"
        >
          <template #default="{ node, data }">
            <span
              class="menu-custom-tree-node"
              tabindex="0"
              @mouseover="handleSelectEleNodeHover(data, node, true)"
              @mouseleave="isNodeHover = false"
              @click="handleSelectEleNodeClick(data, node)"
              @keydown.ctrl.67="handleCtrlC(data)"
              @keydown.ctrl.86="pasteNode(data)"
              :class="{
                'builder-node-root': data.isRoot,
                'builder-node-selected': isCurrentSelected(data),
                'builder-node-allow-drag': allowDrag(node)
              }"
            >
              <JIcon
                class="builder-node-type"
                :class="getNodeType(data.def)"
                :icon="getNodeTypeIcon(data.def, data.isRoot)"
                v-tippy="{ arrow: true, placement: 'left' }"
                :content="getNodeTypeLabel(data.def, data.isRoot)"
              />
              <input
                v-if="data.nameEditing"
                v-model="data.name"
                type="text"
                v-focus
                @blur="handleNameChg(data)"
                @keyup.enter="handleNameChg(data)"
                class="menu-tree-node-input"
              />
              <span
                v-else
                v-text="data.name"
                class="menu-tree-node-title"
                @dblclick="data.nameEditing = true"
              />
              <em
                v-if="data.isRoot"
                v-copy="data.alias"
                v-tippy content="页面组件名，点击复制"
              >-{{ data.alias }}</em>
              <JIcon
                :icon="DFT_ICONS.lifecycle"
                class="builder-node-btn-tip btn-first-tip"
                v-if="!data.isRoot && isMultipleNode(data.def)"
                v-tippy content="循环节点"
              />
            <!--<i class="fa fa-erlang builder-node-btn-tip" v-if="!data.isRoot && data.def && data.def._evts && data.def._evts.length"
               v-tippy="{ arrow: true }" content="节点有事件"></i>-->
              <JIcon
                :icon="DFT_ICONS.bind"
                class="builder-node-btn-tip"
                v-if="isModelNode(data.def)"
                v-tippy content="数据绑定（双向）"
              />
              <JIcon
                :icon="DFT_ICONS.var"
                class="builder-node-btn-tip"
                v-else-if="isDataNode(data.def)"
                v-tippy content="数据展示（显示）"
              /><!-- @click="showNodeProp('data', data)" -->
              <JIcon
                :icon="DFT_ICONS.click"
                class="builder-node-btn-tip"
                v-if="data.def?._evts?.length"
                v-tippy content="已绑事件"
              />
              <el-popconfirm
                title="确定删除当前节点?"
                width="180"
                @confirm="handleRemove(data)"
              >
                <template #reference>
                  <JIcon
                    :icon="DFT_ICONS.delete"
                    class="btn-more builder-node-btn-style"
                  />
                </template>
              </el-popconfirm>
              <JIcon
                :icon="DFT_ICONS.add"
                class="btn-more builder-node-btn-event"
                v-if="!isFixNode(data) && !isLeafNode(data.def)"
                v-tippy content="添加节点"
                @click="showNodeMorePopover($event, data)"
              />
              <JIcon
                :icon="DFT_ICONS.cloud"
                class="btn-more builder-node-btn-data"
                v-if="!isFixNode(data) && !isLeafNode(data.def)"
                v-tippy content="添加云组件"
                @click.stop="handleShowCloudComps(true)"
              />
            </span>
          </template>
        </el-tree><!-- :default-expand-all="true" -->
      </el-scrollbar>
    </div>
    <div class="flex fixed right-0 bottom-0 top-[41px]">
      <div class="ele-design-node-tools page-builder-item-tools">
        <!-- v-if="popData && currentItem && currentItem.page_item_code" -->
        <div class="w-full">
          <div
            class="page-builder-item-tab"
            :class="{ 'active': propTab === 'item' }"
            @click="showNodeProp('item')"
            v-tippy="{ arrow: true, placement: 'left' }"
            content="模块 创建/变量"
          >
            <JIcon :icon="DFT_ICONS.var" class="div" /> 模块变量
            <div class="pt-2 pb-2">/</div>
            <JIcon :icon="DFT_ICONS.comp" class="div" /> 微组件
          </div>
<!--          <div
            class="page-builder-item-tab"
            :class="{ 'active': propTab === 'comp' }"
            @click="showNodeProp('comp')"
            v-tippy="{ arrow: true, placement: 'left' }"
            content="微组件"
          >
            <JIcon :icon="DFT_ICONS.comp" class="div" /> 微组件
          </div>-->
          <div
            class="page-builder-item-tab"
            :class="{ 'active': propTab === 'data' }"
            v-tippy="{ arrow: true, placement: 'left' }" content="节点显示数据"
            @click="showNodeProp('data')"
          >
            <JIcon :icon="DFT_ICONS.setting" class="div" /> 节点设置
          </div>
          <div
            class="page-builder-item-tab"
            :class="{ 'active': propTab === 'style' || propTab === 'hover' }"
            v-tippy="{ arrow: true, placement: 'left' }" content="节点样式"
            @click="showNodeProp('style')"
          >
            <JIcon :icon="DFT_ICONS.css" class="div" /> 节点样式
          </div><!--  @mouseenter="showNodeProp('style', true)"-->

          <div class="flex justify-center items-center h-[40px]"></div>
        </div>

        <div class="pb-1">
          <JIcon
            :icon="DFT_ICONS.arrowLeftSimple"
            class="page-builder-panel-collapse"
            :class="{ 'rotate': nodesPropShow }"
            @click="toggleNodeProp" />
          <!-- :class="{ 'fa-chevron-left': nodesPropShow, 'fa-chevron-right': !nodesPropShow }" -->
        </div>

      </div>
      <BuilderNodesProp
        :class="{ 'expand': nodesPropShow }"
        v-bind="$attrs"
        :current-item="currentItem"
        :current-items="currentItems"
        :current-page="currentPage"
        :current-item-el-code="currentItemElCode"
        :current-node="currentNode"
        :elem-nodes="elemNodes"
        :prop-tab="propTab"
        :pop-data="popData"
        @tabChange="handlePropTabChange"
      />
    </div>

    <el-popover
      :visible="nodeMorePopVisible"
      ref="nodeMorePopoverRef"
      popper-class="popover-dropdown-menu el-dropdown-menu--smalln j-popper"
      placement="right-start"
      width="auto"
      trigger="hover"
      virtual-triggering
      :virtual-ref="nodeMoreTriggerRef"
    >
      <div
        class="w-[303px] h-[600px]"
        @mouseleave="nodeMorePopVisible = false">
        <BuilderNodesAdd
          :current-item="currentItem"
          :current-items="currentItems"
          :pop-data="popData"
          :current-node="currentNode"
          theme="mini"
          @close="hideNodeMorePopover"
          v-bind="$attrs"
        />
      </div>
    </el-popover>

    <el-popover
      ref="nodeActionPopoverRef"
      popper-class="popover-tooltip"
      :visible="nodeActionPopVisible"
      placement="bottom-start"
      :show-arrow="false"
      :offset="5"
      virtual-triggering
      :virtual-ref="nodeActionTriggerRef"
      trigger="manual"
    >
      <div class="design-popover-icons">
        <el-popover
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          v-model:visible="nodeValuePopVisible"
          placement="bottom-start"
          width="auto"
          trigger="hover"
          show-after="200"
          hide-after="50"
        >
          <NodeDataValue
            v-if="nodeValuePopVisible && popData && popData.def"
            v-model="popData.def._value"
            :item="currentItem"
            :page="currentPage"
            :current-ele-data="currentEleData"
            :current-node="currentNode"
            :elem-nodes="elemNodes"
            v-bind="$attrs"
            :disabled="!isDataEnable"
            style="min-width:320px;"
          />
          <template #reference>
            <JIcon :icon="DFT_ICONS.var" class="design-popover-icon"/>
          </template>
        </el-popover>

        <el-popover
          v-if="currentEleData?._type === 'text'"
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          placement="bottom-start"
          width="auto"
          trigger="hover"
          show-after="200"
          hide-after="50"
        >
          <div class="pl-1 pr-1">
            <div
              v-if="currentEleData._value"
              class="font-bold mb-1"
              style="font-size: 12px"
            >
              注：已设置变量，生效静态内容需
              <a href="javascript:;" @click="handleClearTagValue(currentEleData)">清除变量</a>
            </div>
            <el-input
              v-if="currentEleData?._ss"
              v-model="currentEleData._ss.text"
              class="w-[260px]"
              placeholder="请输入内容"
              type="textarea"
              :rows="3"
            />
          </div>
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.text"
              class="design-popover-icon"
            />
          </template>
        </el-popover>

        <el-popover
          v-if="currentEleData?._type === 'icon'"
          popper-class="popover-dropdown-menu el-dropdown-menu--small j-popper"
          placement="bottom-start"
          width="370"
          trigger="hover"
          show-after="200"
          hide-after="50"
        >
<!--          <div>
            <el-button type="primary" plain size="small" @click="handleShowNodeIconWin">重选</el-button>
            <el-button size="small" @click="handleClearNodeProp('icon')">清除</el-button>
          </div>-->
          <IconPanel @select="handleSelectIcon" />
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.icon"
              class="design-popover-icon"
            />
          </template>
        </el-popover>
        <el-popover
          v-if="currentEleData?._type === 'img'"
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          placement="bottom-start"
          width="auto"
          trigger="hover"
          show-after="200"
          hide-after="50"
        >
          <div>
            <el-button
              type="primary"
              plain
              size="small"
              @click="handleShowNodeImgWin"
            >重选
            </el-button>
            <el-button
              size="small"
              @click="handleClearNodeProp('img')"
            >清除
            </el-button>
          </div>
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.image"
              class="design-popover-icon"
              @click="handleShowNodeImgWin"
            />
          </template>
        </el-popover>

        <el-popover
          ref="nodeFormulaPopover"
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          v-model:visible="nodeFormulaPopVisible"
          placement="bottom-start"
          width="auto"
          trigger="hover"
          show-after="200"
          hide-after="50"
        >
          <NodeFormulaRender
            v-if="currentEleData"
            ref="nodeFormulaInput"
            style="padding: 5px 10px;"
            v-model="currentEleData._formula"
            v-bind="$attrs"
            :item="currentItem"
            :page="currentPage"
            :current-ele-data="currentEleData"
            :current-node="currentNode"
            :elem-nodes="elemNodes"
            :disabled="!isDataEnable"
          />
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.formula"
              class="design-popover-icon"
              v-tippy
              :content="`渲染公式: ${ (currentEleData?._formula) || '无' }`"
            />
          </template>
        </el-popover>
        <el-popover
          ref="nodeLowcodePopover"
          v-model:visible="nodeLowcodePopVisible"
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          placement="bottom-start"
          trigger="hover"
          width="300"
          show-after="200"
          hide-after="50"
        >
          <BuilderNodesLowcode
            :current-ele-data="currentEleData"
            :current-page="currentPage"
            v-bind="$attrs"
          />
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.code"
              class="design-popover-icon"
              v-tippy
              content="低码"
            />
          </template>
        </el-popover>

        <el-popover
          ref="nodeEventPopover"
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          v-model:visible="nodeEventPopVisible"
          placement="bottom-start"
          width="auto"
          trigger="hover"
          show-after="200"
          hide-after="50"
        >
          <div
            class="elem-design-prop-logic w-[280px]"
          >
            <h5><span>事件</span></h5>
            <EventTags
              v-model="currentEleData._evts"
              :page="currentPage"
              :page-item-code="currentItem?.page_item_code"
              :support="currentEleData._se"
            />
          </div>
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.click"
              class="design-popover-icon"
              v-tippy
              content="事件"
            />
          </template>
        </el-popover>

        <el-popover
          v-if="isMicroComp(currentEleData)"
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          placement="bottom-start"
          width="auto"
          trigger="hover"
          show-after="200"
          hide-after="50"
        >
          <div
            class="elem-design-prop-logic w-[280px]"
          >
            <h5>扩展属性</h5>
            <BuilderNodePropext
              v-bind="$attrs"
              :ele-def="currentEleData"
              :item="currentItem"
              :page="currentPage"
              :current-ele-data="currentEleData"
              :current-node="currentNode"
              :elem-nodes="elemNodes"
            />
          </div>
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.setting"
              class="design-popover-icon"
              v-tippy
              content="扩展属性"
            />
          </template>
        </el-popover>

        <el-popover
          v-if="!isFixNode(popData) && !isLeafNode(popData?.def)"
          popper-class="popover-dropdown-menu el-dropdown-menu--small"
          placement="right"
          trigger="hover"
          show-after="200"
          hide-after="50"
          width="auto"
        >
          <div class="w-[300px] h-[690px]">
            <BuilderNodesAdd
              :current-item="currentItem"
              :current-items="currentItems"
              :pop-data="popData"
              :current-node="currentNode"
              theme="mini"
              v-bind="$attrs"
            />
          </div>
          <template #reference>
            <JIcon
              :icon="DFT_ICONS.add"
              class="design-popover-icon"
              v-tippy
              content="添加子节点"
            />
          </template>
        </el-popover>

        <JIcon
          :icon="DFT_ICONS.cloud"
          class="design-popover-icon"
          v-tippy
          content="添加云组件"
          @click="handleShowCloudComps(true)"
        />

        <JIcon
          :icon="DFT_ICONS.delete"
          class="design-popover-icon"
          v-tippy
          content="删除"
          @click="handleRemove(popData)"
        />
      </div>
    </el-popover>

    <el-drawer
      v-model="compPanelShow"
      :title="compPanelName"
      :append-to-body="true"
      :with-header="false"
      size="450px"
      direction="btt"
      class="bg-none"
    >
      <BuilderCompsCloud
        v-if="compPanelShow"
        @create-page-comp="handleCreateCloudComp"
      />
    </el-drawer>

    <el-drawer
      v-model="pageItemSelectWinShow"
      title="选择已有页面组件"
      :append-to-body="true"
      :with-header="false"
      size="450px"
      direction="btt"
      class="page-builder-container"
      style="background: none;top: 0px;"
    >
      <PageItemPicker
        :query-page-type="pageType"
        @copyItem="handleCopyItem"
        @referItem="handleReferItem"
        @close="pageItemSelectWinShow = false"
      />
    </el-drawer>
  </div>
</template>

<style lang="scss" scoped>
.ele-design-eles-pop {
  border: none;
  background: none;
  margin: -5px -10px 0px -5px;

  .page-builder-menu-group {
    padding: 0px;
  }

  ul {
    font-size: 12px;

    li {
      margin: 5px 0px 0px 0px;
      width: 100%;
      border-radius: 2px;
      padding: 0px 0px 0px 8px;
      height: 32px;
      background: #F5F9FF;
      font-size: 12px;

      &:hover {
        color: #105EBF;
      }

      .j-icon {
        display: flex;
        width: 14px;
        height: 16px;
        font-size: 14px;
        margin-right: 3px;
      }

      p {
        flex: 1;
        font-weight: bold;
      }

      .action-box {
        width: 26px;
        height: 26px;
        background: #F5F9FA;
        color: #105EBF;
        margin: 1px;
        padding: 5px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-left: 1px solid #EEE;
        opacity: .2;

        &:hover {
          background: #105EBF;
          color: #FFF;
        }
      }

      &:hover {
        .action-box {
          opacity: 1;
        }
      }
    }
  }
}
</style>
